package designpattern;

import lambdasinaction.chap8.ChainOfResponsibilityMain;
import lombok.Data;
import lombok.experimental.Accessors;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;

/**
 * 设计模式
 * @author lilingzhi
 * @since 2022/12/26 9:41
 *
 * 五大模式
 * - 策略模式
 * - 模板模式
 * - 观察者模式
 * - 责任链模式
 * - 工厂模式
 */
public class DesignPatternDemo {



    // Strategy Pattern

    /**
     * 状态模式和策略模式
     * 策略模式：通过自身决策来改变所组合strategy对象
     * 解释：指一系列算法,将每个算个进行封装,进行互换
     */
    @Test
    public void strategyDemo(){
        Validator validator = new Validator(new isNumeric());
        System.out.println(validator.validate("11111"));
    }

    /**
     *  相较于没用lambada表达式写法,更为简便,以及避免代码的僵化
     *  但是没用java8 更为易懂实用
     */
    @Test
    public void strategyLambadaDemo(){
        Validator validator = new Validator(s -> s.matches("[a-z]+"));
        System.out.println(validator.validate("ssss"));
    }


    /**
     * 模板设计模式： 采用某个算法框架，但同时又希望有一定灵活度,能够对某些部分进行改进
     * url:https://www.cnblogs.com/java-my-life/archive/2012/05/14/2495235.html
     **/
    @Test
    public void modelPatternDemo(){
        OnlineBanding onlineBanding = new OnlineBanding() {
            /**
             * 需要实现的方法
             *
             * @param c Customer
             */
            @Override
            void makeCustomerHappy(Customer c) {
                System.out.println("实现makeCustomer,onlineBanding子系统"+c);
            }
        };

        onlineBanding.processCustomer(12);


        onlineBanding.processCustomerLambada(12,c -> System.out.println("实现makeCustomerLambada表达式写法,onlineBanding子系统"+c));
    }

    @Test
    public void ObserverDemo(){
        Feed f = new Feed();
        f.registerObserver(new NYTimes());
        f.registerObserver(new Guardian());
        f.notifyObservers("the queen said her favourite things is money");
    }


    @Test
    public void ObserverLambdaDemo(){
        Feed f = new Feed();
        f.registerObserver((String tweet) ->{
            if (tweet != null && tweet.contains("money")) {
                System.out.println("Breaking news in NY!" + tweet);
            }
        });
        f.registerObserver((String tweet) ->{
            if (tweet != null && tweet.contains("queen")) {
                System.out.println("Yet another news in London...."+tweet);
            }
        });
        f.notifyObservers("the queen said her favourite things is money");
    }


    /**
     *  责任链模式 : 创建处理对象序列的通用方案,
     *  一个处理对象可能需要在完成一些工作之后,
     *  将结果传递到另一个对象,这个对象接着做一些工作,再转交给下一个处理对象
     */
    @Test
    public void processingObject(){
        HeaderTextProcessing p1 = new HeaderTextProcessing();
        SpellCheckerProcessing p2 = new SpellCheckerProcessing();
        InitText p3 = new InitText();

        p1.setSuccessor(p2);
        p2.setSuccessor(p3);

        String result = p1.handle("Aren't labdas really sexy");
        System.out.println(result);
    }


    /**
     * UnaryOperator 相较于 function 函数而言 , 入参和返回结果都是一样的
     */
    @Test
    public void processingObjectLambda(){
        UnaryOperator<String> headProcessing = (String text) -> "From Raoul,Mario and Alan :" + text;
        UnaryOperator<String> spellCheckerProcessing = (String text) -> text.replaceAll("labda", "lambda");
        Function<String, String> pipelline = headProcessing.andThen(spellCheckerProcessing);
        System.out.println(pipelline.apply("Aren't labdas really sexy"));
    }


    @Test
    public void productFactory(){
        Product p1 = ProductFactory.createProduct("loan");
    }

    final static Map<String , Supplier<Product>> map = new HashMap<>();

    static{
        map.put("loan",Loan::new);
        map.put("stock",Stock::new);
        map.put("bond",Bond::new);
    }

    @Test
    public void productFactoryLambda(){
        Supplier<Product> p = map.get("loan");
        if (p != null) {
            Product p1 = p.get();
            System.out.println(p1);
        }
    }





}

// 策略模式
interface  ValidationStrategy{
    boolean execute(String s);
}

class isAllLowerCase implements ValidationStrategy{

    @Override
    public boolean execute(String s) {
        return s.matches("[a-z]+");
    }
}

class isNumeric implements ValidationStrategy{

    @Override
    public boolean execute(String s) {
        return s.matches("\\d+");
    }
}

/**
 * 策略模式
 */
class Validator{
    private final ValidationStrategy strategy;
    public Validator(ValidationStrategy strategy) {
        this.strategy = strategy;
    }

    public boolean validate (String s){
        return strategy.execute(s);
    }
}


/**
 * 模板方法模式:准备一个抽象类，将部分逻辑以具体方法以及具体构造函数的形式实现，
 * 然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法，
 * 从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。
 */
abstract class OnlineBanding {

    public void processCustomer(int id){
        Customer c = Database.getCustomerWithId(12);
        makeCustomerHappy(c);
        hookMethod();
        concreteMethod();
    }

    public void processCustomerLambada(int id ,Consumer<Customer> makeHappy){
        Customer c = Database.getCustomerWithId(12);
        makeHappy.accept(c);
        hookMethod();
        concreteMethod();
    }

    /**
     * 业务方法(已实现)
     */
    private void concreteMethod() {
    }

    /**
     * 基本方法(已实现)
     */
    private void hookMethod() {
    }

    /**
     * 需要实现的方法
     * @param c Customer
     */
    abstract void makeCustomerHappy(Customer c);

}

@Data
class Database{

    public static Customer getCustomerWithId(Integer id){
        return new Customer();
    }
}

class Customer{

}


/**
 * 观察者模式 :
 *
 */
interface Observer{
    void notify(String tweet);
}

class NYTimes implements Observer{

    @Override
    public void notify(String tweet) {
        if (tweet != null && tweet.contains("money")) {
            System.out.println("Breaking news in NY!" + tweet);
        }
    }
}

class Guardian implements Observer{

    @Override
    public void notify(String tweet) {
        if (tweet != null && tweet.contains("queen")) {
            System.out.println("Yet another news in London...."+tweet);
        }
    }
}
interface Subject{
    void registerObserver(Observer o);

    void notifyObservers(String tweet);
}

@Accessors(chain = true)
class Feed implements Subject{
    private final List<Observer> observers = new ArrayList<>();

    @Override
    public void registerObserver(Observer o) {
        this.observers.add(o);
    }

    @Override
    public void notifyObservers(String tweet) {
        observers.forEach(o -> o.notify(tweet));
    }
}


/**
 * 责任链模式
 *
 * @param <T>
 */
abstract class ProcessingObject<T> {
    protected ProcessingObject<T> successor;

    public void setSuccessor(ProcessingObject<T> successor) {
        this.successor = successor;
    }

    public T handle(T input) {
        T r = handleWork(input);
        if (successor != null) {
            return successor.handle(r);
        }
        return r;
    }

    abstract protected T handleWork(T input);
}

class HeaderTextProcessing extends ProcessingObject<String>{

    @Override
    protected String handleWork(String input) {
        return "From Raoul,Mario and Alan :" + input;
    }
}

class SpellCheckerProcessing extends ProcessingObject<String>{

    @Override
    protected String handleWork(String input) {
        return input.replaceAll("labda","lambda");
    }
}

class InitText extends ProcessingObject<String>{

    @Override
    protected String handleWork(String input) {
        return input.split(":")[1];
    }
}


class ProductFactory{
    public static Product createProduct(String name){
        switch (name){
            case "loan" : return new Loan();
            case "stock":return new Stock();
            case "bond":return new Bond();
            default: throw new RuntimeException("No such product " + name);
        }
    }
}


interface Product{}
class Loan implements Product{
    public Loan() {
        System.out.println("loan init ....");
    }
}
class Stock implements Product{
    public Stock() {
        System.out.println("stock init ....");
    }
}
class Bond implements Product{
    public Bond() {
        System.out.println("bond init .....");
    }
}